/* ---<>-----------------------------------
        Includes
---------------------------------------- */



#include <inttypes.h>
#include <vector>
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include "allocations/daq.h"
#include "util/util.h"

using namespace std;
using namespace DAQ_namespace;

timeval refTime;
timeval time1,time2;


DAQALLOC DAQ;

pthread_t primary;  //this is the primary thread.  The program state machine exists here
//  pthread_t transmit; //this thread reads command inputs (LV, Panel GUI etc.)
pthread_t receive;  //this thread handles and buffers incoming Rx data

buf_t bufRx = {(uint8_t *)&bufRx.data[0],(uint8_t *)&bufRx.data[bufferSize-1],(uint8_t *)&bufRx.data[0],(uint8_t *)&bufRx.data[0],0,0,0};

void init(FT_HANDLE handle);


FT_HANDLE handle;
FT_STATUS status;

EVENT_HANDLE eh;
DWORD eventMsk;
DWORD eventWord;


DWORD bytesRead;
DWORD rxBytes;
DWORD txBytes;


uint8_t buffer[4096]; //this is the size of the FTDI driver's buffer

bool DAQ_initialized = false;

void *primaryFunc(void *);
void *receiveFunc(void *);
void *transmitFunc(void *);

pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;

pthread_cond_t  condition_var   = PTHREAD_COND_INITIALIZER;


int main()
{


/* Program concepts:
 *
 * The primary thread is the core of the DAQ program.  This threads jobs are to
 *
 *      1)  Control overall program flow (state machine)
 *      2)  Process incoming data packets
 *
 * The receive thread is used to accumulate data from the FTDI driver.  This data will then
 * be re-populted into a ring buffer.  The ring buffer will be used by the primary thread.
 *
 * The transmit thread in an interface to the user, by the means of some host program such as labview.
 * The host interface will talk to the transmit thread. Incomming conditions will shape the primary
 * thread.
 *
 * ***Note***
 * FTDI supports "custom" special character events.  In the future it may make sense
 * to utilize this functionality to flag incoming packetHeaders specifically.  It may also
 * make sense to then use the receive thread to throw away bad packets then and there.
 *
 */

    //initialize the com port
    handle = comPortInit(1000000,5,5);
    //additions to the initialization routine

    eventMsk = FT_EVENT_RXCHAR; //creates an event mask.  The included conditions will be used to block threads with event conditionals

    FTError(FT_SetEventNotification(handle, eventMsk, (PVOID)&eh)); //I believe this call passes the event conditions to the mutex object

    pthread_mutex_init(&eh.eMutex, NULL);
    pthread_cond_init(&eh.eCondVar, NULL);

    //DAQ init function used to test the DAQ
    init(handle);

    pthread_create(&primary, NULL, &primaryFunc, NULL);
//  pthread_create(&transmit, NULL, &transmitFunc, NULL);
    pthread_create(&receive, NULL, &receiveFunc, NULL);

    pthread_join(primary, NULL);
//  pthread_join(transmit, NULL);
    pthread_join(receive, NULL);

    printf("end of program\r\n");

    exit(0);
}


//the primary function is where data is processed.
//access given to the serial data ring buffer is read only
void *primaryFunc(void *)
{

    //this is the primary program loop
    while(1)
    {
        comPortGet(&bufRx);// parse then process any incoming messages

        if(bufRx.dataOverFlow)
        {
            printf("ring buffer over run\r\n");
            break;
        }
    }

    pthread_mutex_lock(&eh.eMutex);
    comPortClose(handle);
    pthread_mutex_unlock(&eh.eMutex);

    pthread_mutex_lock(&eh.eMutex);
    printf("primary finished\r\n");
    pthread_mutex_unlock(&eh.eMutex);

    return (NULL);
}

//the receive function runs all the time
//when there is incomming data is available it is placed in the ring buffer.
//the receive function has only write access to the serial data ring buffer
void *receiveFunc(void *)
{
    while(1)
    {
        FTError(FT_GetStatus(handle,&rxBytes,&txBytes,&eventWord));
        if(rxBytes > 0)
        {
            status = FT_Read(handle,buffer,rxBytes,&bytesRead);
            while(rxBytes < bytesRead);
            if(status == FT_OK)
            {
                uint16_t i;

                //time critical area halt other threads
                pthread_mutex_lock(&eh.eMutex);

                for(i=0;i<rxBytes;i++)
                {
                    //send data read to the ring buffer
                    bufferWrite(&bufRx,buffer[i]);
                    //printf("0x%X\r\n",buffer[i]);
                }
                pthread_mutex_unlock(&eh.eMutex);
                if(bufRx.dataOverFlow) break;
            }
            else
            {
                printf("read failed");
            }
        }
    }
    pthread_mutex_lock(&eh.eMutex);
    printf("receive finished\r\n");
    pthread_mutex_unlock(&eh.eMutex);
    return (NULL);
}


void *transmitFunc(void *)
{
    //UI/external interface functionality goes here
}









void init(FT_HANDLE handle)
{

DAQ.sendAdcInit(handle,(ADC_CH2_bp | ADC_CH0_bp),ADC_CH_GAIN_1X_gc);

DAQ.sendAdcInit(handle,ADC_CH1_bp,ADC_CH_GAIN_1X_gc);

DAQ.sendAdcInit(handle,ADC_CH3_bp,ADC_CH_GAIN_1X_gc);

DAQ.sendDacEnable(handle,DAC_CH0_bp);

DAQ.sendDigitalInEnable(handle);

DAQ.sendEncoderInit(handle,PLOC_D1,1,0,0,DIGFILT_6SAMPLES_gc,1024);

DAQ.sendEncoderInit(handle,PLOC_D4,0,1,1,DIGFILT_6SAMPLES_gc,1024);

DAQ.sendMotorEnable(handle,0x3);

DAQ.sendServoEnable(handle,0xF);

DAQ.sendSetTimerFreq(handle,500);

DAQ.sendStartProgram(handle);

}

